package utils

import (
	"fmt"
	"reflect"
	"time"

	"github.com/go-viper/mapstructure/v2"
)

// MapstructureDecode
// Title    			MapstructureDecode
// Description   		mapstructure 解析
// Author  				alittlexincan@163.com 					2024/07/17 11:36
// Param    			input 					any     		输入参数
// Param    			output 					any     		输出参数
// Return    			error     								错误信息
func MapstructureDecode(input any, output any) error {
	cfg := &mapstructure.DecoderConfig{
		Result:           &output,
		DecodeHook:       mapstructure.ComposeDecodeHookFunc(DurationHook(), NumericToStringHook()),
		WeaklyTypedInput: true,
	}
	decoder, _ := mapstructure.NewDecoder(cfg)
	return decoder.Decode(input)
}

// DurationHook
// Title    			DurationHook
// Description   		时间转换钩子函数
// Author  				alittlexincan@163.com 					2024/07/17 11:36
// Update  				alittlexincan@163.com 					2024/07/17 11:36
// Return    			mapstructure.DecodeHookFunc     		时间转换钩子函数
func DurationHook() mapstructure.DecodeHookFunc {
	return func(
		from reflect.Type,
		to reflect.Type,
		data interface{},
	) (interface{}, error) {
		if from.Kind() == reflect.String && to == reflect.TypeOf(time.Duration(0)) {
			if s, ok := data.(string); ok {
				return time.ParseDuration(s)
			}
		}
		return data, nil
	}
}

// NumericToStringHook
// Title    			NumericToStringHook
// Description   		数值转换钩子函数
// Author  				alittlexincan@163.com 					2024/07/17 11:36
// Update  				alittlexincan@163.com 					2024/07/17 11:36
// Return    			mapstructure.DecodeHookFunc     		数值转换钩子函数
func NumericToStringHook() mapstructure.DecodeHookFunc {
	return func(from reflect.Kind, to reflect.Kind, data interface{}) (interface{}, error) {
		if to == reflect.String && (from == reflect.Int || from == reflect.Int8 ||
			from == reflect.Int16 || from == reflect.Int32 || from == reflect.Int64 ||
			from == reflect.Uint || from == reflect.Uint8 || from == reflect.Uint16 ||
			from == reflect.Uint32 || from == reflect.Uint64 || from == reflect.Float32 || from == reflect.Float64) {
			return fmt.Sprintf("%v", data), nil
		}
		return data, nil
	}
}
